/*
 * dwr_structs.c
 *
 *  Created on: 20/05/2013
 *      Author: jose
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "dwr_ipoque.h"
#include "dwr_structs.h"
#include "dwr_misc.h"


/******************************************************************
 * "CONSTRUCTORES"
 ******************************************************************/

Dwr_Adj_List *
dwr_adj_list_create()
{
	Dwr_Adj_List * l = (Dwr_Adj_List *) calloc(1, sizeof(Dwr_Adj_List));

	if (!l)
		return NULL;

	/* Asociar funciones */
	l->search = &dwr_adj_list_search_headnode_ptr;
	l->add_head = &dwr_adj_list_headnode_add;
	l->print = &dwr_adj_list_print;

	return l;
}

Dwr_Headnode *
dwr_headnode_create()
{
	Dwr_Headnode * h = (Dwr_Headnode *) calloc(1, sizeof(Dwr_Headnode));

	if (!h)
		return NULL;

	/* Asociar funciones */
	h->compare = &dwr_headnode_key_compare;
	h->update_key = &dwr_headnode_key_update;
	h->add_request = &dwr_headnode_request_add;
	h->add_response = &dwr_headnode_response_add;
	h->print = &dwr_headnode_print;

	return h;
}

Http_Response *
dwr_response_create()
{
	Http_Response * res = (Http_Response *) calloc(1, sizeof(Http_Response));

	if (!res)
		return NULL;

	/* Asociar funciones */
	res->update_text_field = &dwr_response_update;
	res->print = &dwr_response_print;

	return res;
}

Http_Request *
dwr_request_create()
{
	Http_Request * req = (Http_Request *) calloc(1, sizeof(Http_Request));

	if (!req)
		return NULL;

	/* Asociar funciones */
	req->update_text_field = &dwr_request_update;
	req->print = &dwr_request_print;

	return req;
}

/******************************************************************
 * SEARCH
 ******************************************************************/
/*
 * Return:
 * 	Puntero al headnode, en caso de existir
 * 	NULL, en caso de no encontrar el headnode
 */
Dwr_Headnode *
dwr_adj_list_search_headnode_ptr(Dwr_Adj_List * self, void * arg_key)
{
	Dwr_Headnode * ptr_head = NULL;
	ipoque_unique_flow_ipv4_and_6_struct_t * key = (ipoque_unique_flow_ipv4_and_6_struct_t *) arg_key;
	unsigned i;

	assert(self);
	assert(key);

	for (i=1 ; i <= self->heads_len ; i++)
	{
		if (!self->heads[i].compare(&self->heads[i], key))
		{
			ptr_head = &self->heads[i];
			return ptr_head;
		}
	}

	return NULL;
}


/******************************************************************
 * COMPARE
 ******************************************************************/
/*
 * Return:
 * 	0, si key iguales
 * 	1, caso contrario
 */
unsigned
dwr_headnode_key_compare(Dwr_Headnode * self, void * arg_key)
{
	ipoque_unique_flow_ipv4_and_6_struct_t * key = (ipoque_unique_flow_ipv4_and_6_struct_t *) arg_key;

	assert(self);
	assert(key);

	ipoque_unique_flow_ipv4_and_6_struct_t * head_key = (ipoque_unique_flow_ipv4_and_6_struct_t *) self->key;

	if (head_key->lower_port == key->lower_port &&
			head_key->upper_port == key->upper_port &&
			head_key->ip.ipv4.lower_ip == key->ip.ipv4.lower_ip &&
			head_key->ip.ipv4.upper_ip == head_key->ip.ipv4.upper_ip)
		return 0;

	return 1;
}

/******************************************************************
 * ADD
 ******************************************************************/

/*
 * Recibe un key ya creado
 */
Dwr_Headnode *
dwr_adj_list_headnode_add(Dwr_Adj_List * self, void * arg_key)
{
	ipoque_unique_flow_ipv4_and_6_struct_t * key = (ipoque_unique_flow_ipv4_and_6_struct_t *) arg_key;
	Dwr_Headnode * ptr_head = NULL;
	unsigned i;

	assert(self);
	assert(key);

	/* Verificar si ya existe un nodo con el key */
	for (i=1 ; i <= self->heads_len ; i++)
	{
		if (!self->heads[i].compare(&self->heads[i], key))
			return NULL;
	}


	/* Aumentar tamaño del array de heads */
	self->heads = (Dwr_Headnode *) realloc(self->heads, (self->heads_len+2) * sizeof(Dwr_Headnode));

	if (!self->heads)
		return NULL;

	/* Incrementar contador de heads */
	self->heads_len++;

	/* Cerar elementos del headnode recién creado */
	ptr_head = &self->heads[self->heads_len];
	memset(ptr_head, 0, sizeof(self->heads[self->heads_len]));

	/* Asignar funciones al headnode recién creado */
	ptr_head = dwr_headnode_methods_update(ptr_head);

	/* Actualizar key del headnode recién creado */
	ptr_head->update_key(ptr_head, key);

	return ptr_head;
}



/*
 * Recibe un HttpRequest ya creado
 * Return:
 * 	puntero al request recién insertado
 */
Http_Request *
dwr_headnode_request_add(Dwr_Headnode * self, Http_Request * req)
{
	assert(self);
	assert(req);

	if (self->req_len)
	{
		/* Insertar al final */
		self->req_end->next = req;
		self->req_end = self->req_end->next;
	}
	else
		self->req_start = self->req_end = req;

	/* Incrementar contador de http requests */
	self->req_len++;

	return req;
}

/*
 * Recibe un HttpResponse ya creado
 * Return:
 * 	puntero al response recién insertado
 */
Http_Response *
dwr_headnode_response_add(Dwr_Headnode * self, Http_Response * rsp)
{
	assert(self);
	assert(rsp);

	if (self->rsp_len)
	{
		/* Insertar al final */
		self->rsp_end->next = rsp;
		self->rsp_end = self->rsp_end->next;
	}
	else
		self->rsp_start = self->rsp_end = rsp;

	/* Incrementar contador de http requests */
	self->rsp_len++;

	return rsp;
}


/******************************************************************
 * UPDATE
 ******************************************************************/

Dwr_Headnode *
dwr_headnode_key_update(Dwr_Headnode * self, void * arg_key)
{
	ipoque_unique_flow_ipv4_and_6_struct_t * key = (ipoque_unique_flow_ipv4_and_6_struct_t *) arg_key;

	assert(self);
	assert(key);

	self->key = (ipoque_unique_flow_ipv4_and_6_struct_t *) key;

	return self;
}


Dwr_Headnode *
dwr_headnode_methods_update(Dwr_Headnode * h)
{
	assert(h);

	/* Asociar funciones */
	h->compare = &dwr_headnode_key_compare;
	h->update_key = &dwr_headnode_key_update;
	h->add_request = &dwr_headnode_request_add;
	h->add_response = &dwr_headnode_response_add;
	h->print = &dwr_headnode_print;

	return h;
}

/*
 * Actualiza campos que son de texto
 * Param:
 * 	field: nombre del campo a ser cargado
 * 	value: contenido a ser cargado en el campo
 */
Http_Request *
dwr_request_update(Http_Request * req, const char * field, ipoque_int_one_line_struct_t * value)
{
	assert(req);
	assert(field);
	assert(value);

	if (!strcmp(field, "host"))
	{
		req->host = dwr_string_create_with_len(value->ptr, value->len);
		return req;
	}
	if (!strcmp(field, "resource"))
	{
		req->resource = dwr_string_create_with_len(value->ptr, value->len);
		return req;
	}
	if (!strcmp(field, "method"))
	{
		req->method = dwr_string_create_with_len(value->ptr, value->len);
		return req;
	}
	if (!strcmp(field, "referer"))
	{
		req->referer = dwr_string_create_with_len(value->ptr, value->len);
		return req;
	}

	return NULL;
}

/*
 * Actualiza campos que son de texto
 * Param:
 * 	field: nombre del campo a ser cargado
 * 	value: contenido a ser cargado en el campo
 */
Http_Response *
dwr_response_update(Http_Response * rsp, const char * field, ipoque_int_one_line_struct_t * value)
{
	assert(rsp);
	assert(field);
	assert(value);

	if (!strcmp(field, "content_type"))
	{
		rsp->content_type = dwr_string_create_with_len(value->ptr, value->len);
		return rsp;
	}
	if (!strcmp(field, "message"))
	{
		rsp->message = dwr_string_create_with_len(value->ptr, value->len);
		return rsp;
	}

	return NULL;
}

/******************************************************************
 * PRINT
 ******************************************************************/
void
dwr_headnode_print(Dwr_Headnode * h)
{
	assert(h);
	Http_Request * ptr_req = NULL;
	Http_Response * ptr_rsp = NULL;
	unsigned i;

	printf("\n");

	ipoque_unique_flow_ipv4_and_6_struct_t * key =
				(ipoque_unique_flow_ipv4_and_6_struct_t *) h->key;

	dwr_key_print(key);

	printf("\n\treq_len: %u", h->req_len);
	for(ptr_req=h->req_start, i=1; ptr_req ; ptr_req=ptr_req->next, i++)
	{
		printf("\n\t\tRequest %u", i);
		ptr_req->print(ptr_req);
	}

	printf("\n\trsp_len: %u", h->rsp_len);
	for(ptr_rsp=h->rsp_start, i=1; ptr_rsp ; ptr_rsp=ptr_rsp->next, i++)
	{
		printf("\n\t\tResponse %u", i);
		ptr_rsp->print(ptr_rsp);
	}
}

void
dwr_adj_list_print(Dwr_Adj_List * l)
{
	unsigned i;

	assert(l);

	printf("\n");

	printf("\nheads_len: %u", l->heads_len);

	for (i=1 ; i <= l->heads_len ; i++)
	{
		printf("\nHead %u", i);
		l->heads[i].print(&l->heads[i]);
	}

}


void
dwr_request_print(Http_Request * req)
{
	assert(req);

	printf("\n");

	if (req->host)
		printf("\n\t\treq->host %.*s", req->host->len, req->host->text);
	if (req->resource)
		printf("\n\t\treq->resource %.*s", req->resource->len, req->resource->text);
	if (req->method)
		printf("\n\t\treq->method %.*s", req->method->len, req->method->text);
	if (req->referer)
		printf("\n\t\treq->referer %.*s", req->referer->len, req->referer->text);

	printf("\n\t\treq->seq: %u\treq->ack_seq: %u", req->seq, req->ack_seq);
	printf("\n\t\treq->next_tcp_seq_nr[0]: %u\t[1]: %u", req->next_tcp_seq_nr[0],
			req->next_tcp_seq_nr[1]);
	printf("\n\t\tTCP Flags: syn: %u ack: %u: fin: %u psh: %u rst: %u", req->syn,
			req->ack, req->fin, req->psh, req->rst);
}



void
dwr_response_print(Http_Response * rsp)
{
	assert(rsp);

	printf("\n");

	if (rsp->content_type)
		printf("\n\t\trsp->content_type %.*s", rsp->content_type->len, rsp->content_type->text);
	if (rsp->message)
		printf("\n\t\trsp->message %.*s", rsp->message->len, rsp->message->text);

	printf("\n\t\trsp->seq: %u\trsp->ack_seq: %u", rsp->seq, rsp->ack_seq);
	printf("\n\t\trsp->next_tcp_seq_nr[0]: %u\t[1]: %u", rsp->next_tcp_seq_nr[0],
			rsp->next_tcp_seq_nr[1]);
	printf("\n\t\tTCP Flags: syn: %u ack: %u: fin: %u psh: %u rst: %u", rsp->syn,
			rsp->ack, rsp->fin, rsp->psh, rsp->rst);
}
